/*  net_tool.cpp

	v0.0 17/08/1994: MA Initial
	v0.1 28/02/2105: TJ update to VS2013

*****************************************************************************/

#include <windows.h>
#include <stdio.h>
#include <winsock.h>
#include "netTool.h"

#pragma comment(lib,"ws2_32.lib")	// winsockets lib

// globals ... !!! change this if you're multithread !!!
SOCKET sockServer = -1;
SOCKET sockClient = -1;


BOOL firstResponse = true;


/****************************************************************************/
int netTCPserverOpen(u_short tcp_port)
/*
	Globals:
		sockClient: exchange socket
		sockServer: connection socket
	Return:
		 0 : Connection OK
		-1 : WINSOCK.DLL not found
		-2 : cannot open socket
		-3 : address not correct
		-4 : cannot accept
------------------------------------------------------------------------------*/
{
struct sockaddr_in sin;
WORD version = MAKEWORD(1,1);
WSADATA wsa_data;

	// Initialize WINSOCK.DLL
	if(WSAStartup(version, &wsa_data) != 0)
		return -1;

	// Open socket
	if ((sockServer = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		return -2;
	// setsockopt(sockServer,SOL_SOCKET,SO_REUSEADDR,NULL,0);

	// Initialize socket address structure
	memset((char *) &sin, 0, sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = INADDR_ANY;
	sin.sin_port = htons(tcp_port);

	// Set local address
	if ( bind(sockServer, (struct sockaddr*)&sin, sizeof(sin)) < 0)
	{
		closesocket(sockServer);
		return -3;
	}

	// Put socket in listen mode
	if ( listen(sockServer , 5) == -1)
	{
		closesocket(sockServer);
		return -4;
	}

	// Listen
	sockClient = accept(sockServer, (struct sockaddr * )0, (int*)0);
	if (sockClient == -1)
	{
		closesocket(sockServer);
		return -5;
	}

	// OK
	return 0;
}

/****************************************************************************/
int netTCPclientOpen(char *hostname, u_short tcp_port)
/*
	Globals:
		sockClient: exchange socket
	
	Return:
		 0 : Connection OK
		-1 : WINSOCK.DLL not found
		-2 : cannot open socket
		-3 : cannot resolve hostname
		-4 : cannot connect
-----------------------------------------------------------------------------*/
{
struct sockaddr_in sin;
unsigned long net_addr;
struct hostent *hp;

WORD version=MAKEWORD(1,1);
WSADATA	wsa_data;

int nbfails=0;
int status;

	// initialize WINSOCK.DLL
	if (WSAStartup(version, &wsa_data) != 0)
		return -1;

	// Open socket
	if ((sockClient = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		return -2;
	// setsockopt(sockClient, SOL_SOCKET, SO_REUSEADDR, NULL, 1);

	// Initialize server address
	memset((char *) &sin, 0, sizeof(sin));
	sin.sin_family = AF_INET;
	net_addr = inet_addr(hostname);
	// hostname is not an IP
	if (net_addr == INADDR_NONE)
	{
		// name to IP resolution
		hp = gethostbyname(hostname);
		if (hp == NULL)
		{
			printf("erreur = %d", WSAGetLastError());
			closesocket(sockClient);
			return -3;
		}
		memcpy(&net_addr,hp->h_addr_list[0],hp->h_length);
	}
	sin.sin_addr.s_addr = net_addr;
	sin.sin_port = htons(tcp_port);

	// connect ... try 5 times
	while (((status = connect(sockClient, (struct sockaddr far*)&sin, sizeof(sin))) < 0) && (nbfails < 5))
		nbfails++;

	// valid Connection ?
	if (status < 0)
	{
		closesocket(sockClient);
		return -4;
	}

	// OK
	return 0;
}

const struct timeval timeout { TIMEOUT, 0 };



/****************************************************************************/
int netSend(char *sendbuf, int length)
/*
Global :
sockClient: communication socket
Return:
0 :  OK
-1 :  ERROR
------------------------------------------------------------------------------*/
{
	// send everything
	return send(sockClient, (char *)(sendbuf), length, 0);
}
/****************************************************************************/
int netReceive(char *readbuf, int length)
/*
	Global:
		sockClient: communication socket 
	Retourne:
		 n : received n bytes
		 0 : empty message (... finished)
		-1 : no message
------------------------------------------------------------------------------*/
{
	// receive one packet (you may need to loop this call in a TCP client)

	//Use select() to determine if the socket's peer is still sending
	FD_SET ReadSet;
	FD_ZERO(&ReadSet);
	FD_SET(sockClient, &ReadSet);

	if (!firstResponse)
	{
		//Check if anything else to receive
		if(! select(0, &ReadSet, NULL, NULL, &timeout))
			return 0;
	}
	//Blocking receive
	int numBytes =  recv(sockClient, readbuf, length, 0);
	if (numBytes > 0 )
		firstResponse = false;
	return numBytes;
	
}

/****************************************************************************/
/*
Global:
sockClient: communication socket
------------------------------------------------------------------------------*/
void netDoneSend()
{
	// Called after all send messages are sent
	// Do blocking receive on first call to get response, assuming OK to read from socket
	// Then with timeout after that checking for read OK
	firstResponse = true;
}

/****************************************************************************/
int netPeek(char *readbuf, int length)
/*
Global:
sockClient: communication socket
Retourne:
n : received n bytes
0 : empty message (... finished)
-1 : no message
------------------------------------------------------------------------------*/
{
	// receive one packet (you may need to loop this call in a TCP client)
	return recv(sockClient, readbuf, length, MSG_PEEK);
}

/****************************************************************************/
int netClose(void)
/*

	Global:
		sockClient, sockServer
	Return:
		 0 : OK
		-1 : Error
------------------------------------------------------------------------------*/
{
	// close server socket if opened
	if (sockServer != -1)
		closesocket(sockServer);

	// close client socket if opened
	if (sockClient != -1)
		closesocket(sockClient);

	return 0;
}

/****************************************************************************/
int netClean(void)
/*
	Global:
		none
	Return:
		0 : purge OK
		? : WSA error
-------------------------------------------------------------------------------*/
{
	return WSACleanup();
}